package Micelles;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;


public class ImageManipulator {

	final static double[][] CFILTER_BLUR_1 = new double[3][3];
	final static double[][] CFILTER_BLUR_2 = new double[3][3];
	final static double[][] CFILTER_SHARPEN_1 = new double[3][3];
	final static double[][] CFILTER_SHARPEN_2 = new double[3][3];
	final static double[][] CFILTER_EDGEENHANCE_1 = new double[3][3];
	final static double[][] CFILTER_EDGEENHANCE_2 = new double[3][3];
	final static double[][] CFILTER_EDGEENHANCE_3 = new double[3][3];
	final static double[][] CFILTER_FINDEDGE_1 = new double[3][3];
	final static double[][] CFILTER_FINDEDGE_2 = new double[3][3];
	final static double[][] CFILTER_FINDEDGE_3 = new double[3][3];
	final static double[][] CFILTER_EMBOSS = new double[3][3];
	
	BufferedImage image;
	
	int[] frequencies = new int[254];
	
	public ImageManipulator(BufferedImage i){
		
		image = i;
		
		for(int count = 0; count < CFILTER_BLUR_1.length*3; count++){
			
			CFILTER_BLUR_1[count%3][count/3] = 1;
		}
		
		CFILTER_BLUR_2[0][0] = 1;
		CFILTER_BLUR_2[0][1] = 2;
		CFILTER_BLUR_2[0][2] = 1;
		CFILTER_BLUR_2[1][0] = 2;
		CFILTER_BLUR_2[1][1] = 4;
		CFILTER_BLUR_2[1][2] = 2;
		CFILTER_BLUR_2[2][0] = 1;
		CFILTER_BLUR_2[2][1] = 2;
		CFILTER_BLUR_2[2][2] = 1;

		CFILTER_SHARPEN_1[0][0] = -1;
		CFILTER_SHARPEN_1[0][1] = -1;
		CFILTER_SHARPEN_1[0][2] = -1;
		CFILTER_SHARPEN_1[1][0] = -1;
		CFILTER_SHARPEN_1[1][1] = 9;
		CFILTER_SHARPEN_1[1][2] = -1;
		CFILTER_SHARPEN_1[2][0] = -1;
		CFILTER_SHARPEN_1[2][1] = -1;
		CFILTER_SHARPEN_1[2][2] = -1;

		CFILTER_SHARPEN_2[0][0] = 0;
		CFILTER_SHARPEN_2[0][1] = -1;
		CFILTER_SHARPEN_2[0][2] = 0;
		CFILTER_SHARPEN_2[1][0] = -1;
		CFILTER_SHARPEN_2[1][1] = 5;
		CFILTER_SHARPEN_2[1][2] = -1;
		CFILTER_SHARPEN_2[2][0] = 0;
		CFILTER_SHARPEN_2[2][1] = -1;
		CFILTER_SHARPEN_2[2][2] = 0;

		CFILTER_EDGEENHANCE_1[0][0] = 0;
		CFILTER_EDGEENHANCE_1[0][1] = -1;
		CFILTER_EDGEENHANCE_1[0][2] = 0;
		CFILTER_EDGEENHANCE_1[1][0] = 0;
		CFILTER_EDGEENHANCE_1[1][1] = 1;
		CFILTER_EDGEENHANCE_1[1][2] = 0;
		CFILTER_EDGEENHANCE_1[2][0] = 0;
		CFILTER_EDGEENHANCE_1[2][1] = 0;
		CFILTER_EDGEENHANCE_1[2][2] = 0;

		CFILTER_EDGEENHANCE_2[0][0] = 0;
		CFILTER_EDGEENHANCE_2[0][1] = 0;
		CFILTER_EDGEENHANCE_2[0][2] = 0;
		CFILTER_EDGEENHANCE_2[1][0] = -1;
		CFILTER_EDGEENHANCE_2[1][1] = 1;
		CFILTER_EDGEENHANCE_2[1][2] = 0;
		CFILTER_EDGEENHANCE_2[2][0] = 0;
		CFILTER_EDGEENHANCE_2[2][1] = 0;
		CFILTER_EDGEENHANCE_2[2][2] = 0;

		CFILTER_EDGEENHANCE_3[0][0] = -1;
		CFILTER_EDGEENHANCE_3[0][1] = 0;
		CFILTER_EDGEENHANCE_3[0][2] = 0;
		CFILTER_EDGEENHANCE_3[1][0] = 0;
		CFILTER_EDGEENHANCE_3[1][1] = 1;
		CFILTER_EDGEENHANCE_3[1][2] = 0;
		CFILTER_EDGEENHANCE_3[2][0] = 0;
		CFILTER_EDGEENHANCE_3[2][1] = 0;
		CFILTER_EDGEENHANCE_3[2][2] = 0;
		
		CFILTER_FINDEDGE_1[0][0] = 0;
		CFILTER_FINDEDGE_1[0][1] = 1;
		CFILTER_FINDEDGE_1[0][2] = 0;
		CFILTER_FINDEDGE_1[1][0] = 1;
		CFILTER_FINDEDGE_1[1][1] = -4;
		CFILTER_FINDEDGE_1[1][2] = 1;
		CFILTER_FINDEDGE_1[2][0] = 0;
		CFILTER_FINDEDGE_1[2][1] = 1;
		CFILTER_FINDEDGE_1[2][2] = 0;

		CFILTER_FINDEDGE_2[0][0] = -1;
		CFILTER_FINDEDGE_2[0][1] = -1;
		CFILTER_FINDEDGE_2[0][2] = -1;
		CFILTER_FINDEDGE_2[1][0] = -1;
		CFILTER_FINDEDGE_2[1][1] = 8;
		CFILTER_FINDEDGE_2[1][2] = -1;
		CFILTER_FINDEDGE_2[2][0] = -1;
		CFILTER_FINDEDGE_2[2][1] = -1;
		CFILTER_FINDEDGE_2[2][2] = -1;

		CFILTER_FINDEDGE_3[0][0] = 1;
		CFILTER_FINDEDGE_3[0][1] = -2;
		CFILTER_FINDEDGE_3[0][2] = 1;
		CFILTER_FINDEDGE_3[1][0] = -2;
		CFILTER_FINDEDGE_3[1][1] = 4;
		CFILTER_FINDEDGE_3[1][2] = -2;
		CFILTER_FINDEDGE_3[2][0] = 1;
		CFILTER_FINDEDGE_3[2][1] = -2;
		CFILTER_FINDEDGE_3[2][2] = 1;

		CFILTER_EMBOSS[0][0] = -2;
		CFILTER_EMBOSS[0][1] = -1;
		CFILTER_EMBOSS[0][2] = 0;
		CFILTER_EMBOSS[1][0] = -1;
		CFILTER_EMBOSS[1][1] = 1;
		CFILTER_EMBOSS[1][2] = 1;
		CFILTER_EMBOSS[2][0] = 0;
		CFILTER_EMBOSS[2][1] = 1;
		CFILTER_EMBOSS[2][2] = 2;
		
	}
	
	public BufferedImage applyFilter(double[][] filter, double divisor, double bias, int iterations){

		if(filter == null){
			return image;
		}
		
		int redSum, blueSum, greenSum, red, green, blue, x, y;
		Color temp;
		Color[][] newImageVals = new Color[image.getWidth()][image.getHeight()];

		BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
		

		for(int j = 0; j < newImage.getHeight(); j++){
			for(int i = 0; i < newImage.getWidth(); i++){
			
				newImage.setRGB(i, j, image.getRGB(i, j));
			}
		}
		
		for(int index = 0; index < iterations; index++){

			for(int j = 0; j < newImage.getHeight(); j++){
				for(int i = 0; i < newImage.getWidth(); i++){

					redSum = 0;
					blueSum = 0;
					greenSum = 0;
					
					for(int count = 0; count < filter.length*filter.length; count++){

						x = i-filter.length/2+(count%filter.length);
						y = j-filter.length/2+(count/filter.length);
						if(x < 0 || y < 0 || x >= newImage.getWidth() || y >= newImage.getHeight()){
							temp = new Color(0, 0, 0);
						}
						else{
							temp = new Color(newImage.getRGB(x, y));
						}
						redSum += filter[count%filter.length][count/filter.length]*temp.getRed();
						blueSum += filter[count%filter.length][count/filter.length]*temp.getBlue();
						greenSum += filter[count%filter.length][count/filter.length]*temp.getGreen();
					}

					red = (int)(redSum/divisor + bias);
					blue = (int)(blueSum/divisor + bias);
					green = (int)(greenSum/divisor + bias);
					if(red < 0){
						red = 0;
					}
					else if(red > 255){
						red = 255;
					}
					if(blue < 0){
						blue = 0;
					}
					else if(blue > 255){
						blue = 255;
					}
					if(green < 0){
						green = 0;
					}
					else if(green > 255){
						green = 255;
					}

					newImageVals[i][j] = new Color(red, green, blue);

				}
			}


			for(int j = 0; j < newImage.getHeight(); j++){
				for(int i = 0; i < newImage.getWidth(); i++){

					newImage.setRGB(i, j, newImageVals[i][j].getRGB());
				}
			}

		}
		
		return newImage;
	}
	
	public BufferedImage gaussianBlur(int kernel, double stdev, int iterations){
		
		double[][] filter = new double[kernel][kernel];
		double divisor = 0;
		int i, j;
		
		for(int y = 0; y < kernel; y++){
			for(int x = 0; x < kernel; x++){
				
				i = x-(kernel/2);
				j = y-(kernel/2);
				filter[x][y] = (1/(2*Math.PI*stdev*stdev))*Math.exp(-(i*i+j*j)/(2*stdev*stdev));
				divisor += filter[x][y];
			}
		}
		
		return applyFilter(filter, divisor, 0, iterations);
	}
	
	public void saveImage(File location, String name, BufferedImage i){

		try {
			File rendered = new File(location.getAbsolutePath(), name + ".png");//location.getAbsolutePath(), name + ".png");
			ImageIO.write(i, "png", rendered);
		} catch (IOException e) {
			System.out.println(e);
		}
	}

	public void gatherFrequencies(){
		
		for(int count = 0; count < frequencies.length; count++){
			
			frequencies[count] = 0;
		}
		
		Color temp;
		for(int j = 0; j < image.getHeight(); j++){
			for(int i = 0; i < image.getWidth(); i++){

				temp = new Color(image.getRGB(i, j));
				if(temp.getGreen() != 0 &&
						temp.getGreen() != 255){
					frequencies[temp.getGreen()-1]++;
				}
			}
		}
	}
	
	public void drawHistogram(File location, String name, int scale){

		gatherFrequencies();
		
		int max = 0;
		
		for(int count = 0; count < frequencies.length; count++){
			
			if(frequencies[count] > max){
				max = frequencies[count];
			}
		}

		int resolution = (max/scale + 30)/254;
		
		BufferedImage i = new BufferedImage(254*resolution, max/scale + 30, BufferedImage.TYPE_INT_RGB);
		
		for(int x = 0; x < i.getWidth(); x+=resolution){
			for(int y = 0; y < i.getHeight(); y++){
				
				if(y <= frequencies[x/resolution]/scale){
					for(int count = 0; count < resolution; count++){

						i.setRGB(x+count, (i.getHeight()-1) - y, (new Color(0, 0, 0)).getRGB());
					}
				}
				else{
					for(int count = 0; count < resolution; count++){

						i.setRGB(x+count, (i.getHeight()-1) - y, (new Color(255, 255, 255)).getRGB());
					}
				}
			}
		}
		
		try {
			File rendered = new File(location.getAbsolutePath(), name + ".png");//location.getAbsolutePath(), name + ".png");
			ImageIO.write(i, "png", rendered);
		} catch (IOException e) {
			System.out.println(e);
		}
		
		
	}
}
